#pragma once
#include <map>
#include <memory>
#include <functional>
#include <nice2d/application.h>

#define STR(str) #str
#define A_B(a,b) a##b
#define A_B_C(a,b,c) a##b##c

template<class Product,class ID>
class ObjectFactory
{
public:
    static std::shared_ptr<Product> create(const ID &id)
    {
        auto itr = creator->find(id);
        if (itr != creator->end())
            return std::shared_ptr<Product>(itr->second());
        return nullptr;
    }

    static std::map<ID,std::function<std::shared_ptr<Product>()>> *creator;

    static void initialize()
    {
        if (!creator) {
            creator = new std::map<ID,std::function<std::shared_ptr<Product>()>>();

            Application::getInstance()->addExitCallback([&]() {
                shutdown();
            });
        }
    }

    static void shutdown()
    {
        if (creator)
            delete creator;
        creator = nullptr;
    }
};

template<class Product, class Target, class ID>
class ObjectRegister : public ObjectFactory<Product, ID>
{
public:
    ObjectRegister(const ID &id)
    {
        ObjectFactory<Product, ID>::initialize();
        (*ObjectFactory<Product, ID>::creator)[id] = []()->std::shared_ptr<Product>{
            return std::make_shared<Target>();
        };
    }
};

template<class Product, class ID>
std::map<ID,std::function<std::shared_ptr<Product>()>>* ObjectFactory<Product, ID>::creator = nullptr;

#define REGISTER_OBJECT(id,base,product)\
    ObjectRegister<base,product,int32_t> A_B(base,product)(id);

#define REGISTER_OBJECT_WITH_TYPE(type,key,base,product) ObjectRegister<base,product,type> A_B(base,product)(key);
